package com.cyy.learn.game.x2048

import java.util.ArrayList
import javafx.animation.*
import javafx.beans.value.ChangeListener
import javafx.beans.value.ObservableValue
import javafx.event.ActionEvent
import javafx.event.EventHandler
import javafx.scene.control.Alert
import javafx.scene.input.KeyCode
import javafx.scene.input.KeyEvent
import javafx.scene.input.MouseEvent
import javafx.scene.input.SwipeEvent
import javafx.scene.layout.Pane
import javafx.scene.layout.StackPane
import javafx.scene.paint.Color
import javafx.scene.shape.Rectangle
import javafx.scene.text.Font
import javafx.scene.text.Text
import javafx.util.Duration
import kotlin.math.abs
import kotlin.math.pow
import kotlin.math.roundToLong

object X2048Service {
    var size: Int = 0
    private var table: Array<IntArray>? = null
    private var tableold: Array<IntArray>? = null
    private var tableFlight: Array<Array<Point>>? = null
    private var tableAfter: Array<IntArray>? = null
    private val tableAdd = arrayOfNulls<Point>(2)
    private var state: State? = null
    private var keyPressed: Boolean = false
    private var startFlag = false
    private var code: KeyCode? = null
    private val paneList = ArrayList<Pane>()
    var width: Double = 0.0
    var height: Double = 0.0
    var margin = 0
    var scoreInt = 0
    var mMousePressed: Boolean = false
    private val fontName = "System"
    private val color = arrayOf<Color>(Color.TRANSPARENT, Color.rgb(255, 236, 158), Color.rgb(255, 193, 0), Color.rgb(255, 81, 0), Color.rgb(255, 0, 92), Color.rgb(185, 0, 255), Color.rgb(122, 0, 255), Color.rgb(0, 60, 255), Color.rgb(0, 207, 255), Color.rgb(0, 255, 99), Color.rgb(126, 255, 0), Color.rgb(190, 255, 0))
    var m2048ColorTransition: Transition? = null
    var mPressedPoint: Point? = null
    var mReleasedPoint: Point? = null

    fun implInit(size: Int) {
        this.size = size
        startFlag = true
        scoreInt = 0
        state = State.RUNNING
        table = Array(size) { IntArray(size) }
        tableFlight = Array(size) { Array<Point>(size) { _: Int -> Point(0, 0) } }
        tableAfter = Array(size) { IntArray(size) }
        for (i in 0 until size) {
            for (j in 0 until size) {
                tableFlight!![i][j] = Point(0, 0)
            }
        }
        playArea.children.clear()
        paneList.clear()
        width = playArea.width
        height = playArea.height
        if (width > height) {
            width = height
        } else {
            height = width
        }
        val bk = Rectangle()
        when(size){
            3->margin = 20
            4->margin = 15
            5->margin = 10
            6->margin = 6
        }
        bk.width = width - margin / 2
        bk.height = height - margin / 2
        bk.fill = Color.grayRgb(195)
        bk.arcWidth = 10.0
        bk.arcHeight = 10.0
        playArea.children.add(bk)

        for (i in 0 until size) {
            for (j in 0 until size) {
                val pane = StackPane()
                val r = Rectangle()
                r.fill = Color.grayRgb(225)
                r.stroke = Color.TRANSPARENT
                pane.layoutX = j * (width - margin) / size + margin / 2 - 1
                pane.layoutY = i * (height - margin) / size + margin / 2 - 1
                pane.prefWidth = (width - margin) / size - margin / 2 + 2
                pane.prefHeight = (height - margin) / size - margin / 2 + 2
                r.width = (width - margin) / size - margin / 2 + 2
                r.height = (height - margin) / size - margin / 2 + 2
                pane.children.add(r)
                playArea.children.add(pane)
            }
        }
        for (i in 0 until size) {
            for (j in 0 until size) {
                val pane = StackPane()
                val r = Rectangle()
                r.fill = color[0]
                r.stroke = Color.TRANSPARENT
                r.arcHeight = 30.0
                r.arcWidth = 30.0
                val text = Text("")
                text.fill = Color.BLACK
                when(size){
                    3->{
                        text.font = Font(fontName, 45.0)
                        r.arcHeight = 40.0
                        r.arcWidth = 40.0
                    }
                    4->{
                        text.font = Font(fontName, 40.0)
                        r.arcHeight = 30.0
                        r.arcWidth = 30.0
                    }
                    5->{
                        text.font = Font(fontName, 35.0)
                        r.arcHeight = 20.0
                        r.arcWidth = 20.0
                    }
                    6->{
                        text.font = Font(fontName, 25.0)
                        r.arcHeight = 10.0
                        r.arcWidth = 10.0
                    }
                }

                pane.layoutX = j * (width - margin) / size + margin / 2
                pane.layoutY = i * (height - margin) / size + margin / 2
                pane.prefWidth = (width - margin) / size - margin / 2
                pane.prefHeight = (height - margin) / size - margin / 2
                r.width = (width - margin) / size - margin / 2
                r.height = (height - margin) / size - margin / 2
                pane.children.add(r)
                pane.children.add(text)
                paneList.add(pane)
                playArea.children.add(pane)

            }
        }
        afterMove()
    }

    private fun render() {
        var add = 1
        if (startFlag) {
            add = 2
            startFlag = false
        }
        val parallelTransition2 = ParallelTransition()
        val list = ArrayList<MoveAnime>()
        for (i in 0 until size) {
            for (j in 0 until size) {
                val pane = paneList[i * size + j] as StackPane
                if (tableFlight != null) {
                    if (tableFlight!![i][j].my != 0 || tableFlight!![i][j].mx != 0) {
                        list.add(MoveAnime(this, pane, tableFlight!![i][j].mx, tableFlight!![i][j].my))
                    }
                }
                if (tableAfter != null && tableAfter!![i][j] != 0) {
                    val scaleTransition = ScaleTransition(Duration.millis(100.0), pane)
                    scaleTransition.fromX = 1.15
                    scaleTransition.fromY = 1.15
                    scaleTransition.toX = 1.0
                    scaleTransition.toY = 1.0
                    parallelTransition2.children.add(scaleTransition)
                }

                for (k in 0 until add) {
                    if (i == tableAdd[k]?.mx && j == tableAdd[k]?.my) {
                        val scaleTransition = ScaleTransition(Duration.millis(100.0), pane)
                        scaleTransition.fromX = 0.85
                        scaleTransition.fromY = 0.85
                        scaleTransition.toX = 1.0
                        scaleTransition.toY = 1.0
                        parallelTransition2.children.add(scaleTransition)
                    }
                }
            }
        }
        startMoveAnime(list)
        val s = SequentialTransition()
        val pauseTransition = PauseTransition(Duration.millis(100.0))
        s.children.addAll(pauseTransition, parallelTransition2)
        s.play()
    }

    private fun startMoveAnime(list: List<MoveAnime>) {
        val animation = object : Transition() {
            init {
                cycleDuration = Duration.millis(100.0)
            }

            override fun interpolate(frac: Double) {
                for (item in list) {
                    item.mPane.layoutX = item.mFromX + frac * item.mMoveX
                    item.mPane.layoutY = item.mFromY + frac * item.mMoveY
                }
            }
        }
        animation.onFinished = EventHandler<ActionEvent> {
            for (i in 0 until size) {
                for (j in 0 until size) {
                    val pane = paneList.get(i * size + j) as StackPane
                    pane.layoutX = j * (width - margin) / size + margin / 2
                    pane.layoutY = i * (height - margin) / size + margin / 2
                    val r = pane.children.get(0) as Rectangle
                    val t = pane.children.get(1) as Text
                    r.fill = color[table!![i][j]]
                    if (table!![i][j] != 0) {
                        t.text = String.format("%d", 2.0.pow(table!![i][j].toDouble()).toInt())
                    } else {
                        t.text = ""
                    }
                }
            }
        }
        animation.play()
    }

    private fun addRandomBlock() {
        var add = 1
        if (startFlag) {
            add = 2
        }
        val list = checkEmptyArea()
        if (list.size != 0) {
            for (j in 0 until add) {
                val i = (Math.random() * list.size).toInt()
                val k = list[i]
                table!![k / size][k % size] = 1
                list.removeAt(i)
                tableAdd[j] = Point(k / size, k % size)

            }
        }
    }

    private fun checkEmptyArea(): MutableList<Int> {
        val list = ArrayList<Int>()
        for (i in 0 until size) {
            for (j in 0 until size) {
                if (table!![i][j] == 0) {
                    list.add(i * size + j)
                }
            }
        }
        return list
    }

    fun onKeyPressed(keyEvent: KeyEvent) {
        if (!keyPressed) {
            keyPressed = true
            code = keyEvent.code
        }
    }

    fun onKeyReleased(event: KeyEvent) {
        if (keyPressed) {
            keyPressed = false
            if (event.code === code) {
                processCode(code)
            }
        }
    }

    private fun onChange(): Boolean {
        if (state == State.RUNNING) {
            for (i in 0 until size) {
                for (j in 0 until size) {
                    if (table!![i][j] != tableold!![i][j]) {
                        return true
                    }
                }
            }
        }
        return false
    }

    fun processCode(code: KeyCode?){
        tableold = Array(size) { IntArray(size) }
        tableAfter = Array(size) { IntArray(size) }
//        tableFlight = Array(size) { arrayOfNulls(size) }
        for (i in 0 until size) {
            System.arraycopy(table!![i], 0, tableold!![i], 0, size)
        }
        for (i in 0 until size) {
            for (j in 0 until size) {
                tableFlight!![i][j] = Point(0, 0)
                tableAfter!![i][j] = 0
            }
        }
        val l: IntArray
        when (code) {
            KeyCode.W,KeyCode.UP-> {
                l = IntArray(size)
                for (i in 0 until size) {
                    l[i] = 0
                }
                for (i in 1 until size) {
                    for (j in 0 until size) {
                        if (table!![i][j] != 0) {
                            var k = i - 1
                            while (k >= l[j]) {
                                if (table!![k + 1][j] == table!![k][j]) {
                                    table!![k + 1][j] = 0
                                    table!![k][j]++
                                    l[j] = k + 1
                                    tableFlight!![i][j].my--
                                    tableAfter!![k][j] = 1
                                } else if (table!![k + 1][j] != 0 && table!![k][j] == 0) {
                                    table!![k][j] = table!![k + 1][j]
                                    table!![k + 1][j] = 0
                                    tableFlight!![i][j].my--
                                }
                                k--
                            }

                        }
                    }
                }
                if (onChange()) {
                    afterMove()
                }
            }
            KeyCode.S,KeyCode.DOWN -> {
                l = IntArray(size)
                for (i in 0 until size) {
                    l[i] = size - 1
                }
                for (i in size - 2 downTo 0) {
                    for (j in 0 until size) {
                        if (table!![i][j] != 0) {
                            var k = i + 1
                            while (k <= l[j]) {
                                if (table!![k - 1][j] == table!![k][j]) {
                                    table!![k - 1][j] = 0
                                    table!![k][j]++
                                    l[j] = k - 1
                                    tableFlight!![i][j].my++
                                    tableAfter!![k][j] = 1
                                } else if (table!![k - 1][j] != 0 && table!![k][j] == 0) {
                                    table!![k][j] = table!![k - 1][j]
                                    table!![k - 1][j] = 0
                                    tableFlight!![i][j].my++
                                }
                                k++
                            }
                        }
                    }
                }
                if (onChange()) {
                    afterMove()
                }
            }
            KeyCode.A,KeyCode.LEFT -> {
                l = IntArray(size)
                for (i in 0 until size) {
                    l[i] = 0
                }
                for (j in 1 until size) {
                    for (i in 0 until size) {
                        if (table!![i][j] != 0) {
                            var k = j - 1
                            while (k >= l[i]) {
                                if (table!![i][k] == table!![i][k + 1]) {
                                    table!![i][k + 1] = 0
                                    table!![i][k]++
                                    l[i] = k + 1
                                    tableFlight!![i][j].mx--
                                    tableAfter!![i][k] = 1
                                } else if (table!![i][k + 1] != 0 && table!![i][k] == 0) {
                                    table!![i][k] = table!![i][k + 1]
                                    table!![i][k + 1] = 0
                                    tableFlight!![i][j].mx--
                                }
                                k--
                            }

                        }
                    }
                }
                if (onChange()) {
                    afterMove()
                }
            }
            KeyCode.D,KeyCode.RIGHT -> {
                l = IntArray(size)
                for (i in 0 until size) {
                    l[i] = size - 1
                }
                for (j in size - 2 downTo 0) {
                    for (i in 0 until size) {
                        if (table!![i][j] != 0) {
                            var k = j + 1
                            while (k <= l[i]) {
                                if (table!![i][k] == table!![i][k - 1]) {
                                    table!![i][k - 1] = 0
                                    table!![i][k]++
                                    l[i] = k - 1
                                    tableFlight!![i][j].mx++
                                    tableAfter!![i][k] = 1
                                } else if (table!![i][k - 1] != 0 && table!![i][k] == 0) {
                                    table!![i][k] = table!![i][k - 1]
                                    table!![i][k - 1] = 0
                                    tableFlight!![i][j].mx++
                                }
                                k++
                            }
                        }
                    }
                }
                if (onChange()) {
                    afterMove()
                }
            }
        }

    }

    private fun afterMove() {
        addRandomBlock()
        addScore()
        if (checkLose()) {
            state = State.GAMEOVER
        }
        if (check2048()) {
            state = State.GAMESUCCESS
        }

        render()
        if (state == State.GAMEOVER) {
            val alert = Alert(Alert.AlertType.INFORMATION)
            alert.title = "抱歉"
            alert.headerText = "游戏结束"
            alert.contentText = "xwintop，欢迎支持！\n请点击按钮重置游戏！"
            alert.show()
        } else if (state == State.GAMESUCCESS) {
            val alert = Alert(Alert.AlertType.INFORMATION)
            alert.title = "666"
            alert.headerText = "大吉大利，晚上吃鸡"
            alert.contentText = "你赢了？--xwintop。\n请点击按钮重置游戏！"
            alert.show()
        }
    }

    private fun check2048(): Boolean {
        for (i in 0 until size) {
            for (j in 0 until size) {
                if (table!![i][j] == 11) {
                    val pane = paneList.get(i * size + j) as StackPane
                    val rectangle = pane.children[0] as Rectangle
                    val timeline = Timeline()
                    val values = arrayOf(KeyValue(rectangle.fillProperty(), Color.rgb(255, 0, 0)), KeyValue(rectangle.fillProperty(), Color.rgb(255, 125, 0)), KeyValue(rectangle.fillProperty(), Color.rgb(255, 255, 0)), KeyValue(rectangle.fillProperty(), Color.rgb(0, 255, 0)), KeyValue(rectangle.fillProperty(), Color.rgb(0, 255, 255)), KeyValue(rectangle.fillProperty(), Color.rgb(0, 0, 255)), KeyValue(rectangle.fillProperty(), Color.rgb(255, 0, 255)), KeyValue(rectangle.fillProperty(), Color.rgb(255, 0, 0)))
                    val frames = arrayOf(KeyFrame(Duration.millis(0.0), values[0]), KeyFrame(Duration.millis(200.0), values[1]), KeyFrame(Duration.millis(400.0), values[2]), KeyFrame(Duration.millis(600.0), values[3]), KeyFrame(Duration.millis(800.0), values[4]), KeyFrame(Duration.millis(1000.0), values[5]), KeyFrame(Duration.millis(1200.0), values[6]), KeyFrame(Duration.millis(1400.0), values[7]))
                    timeline.keyFrames.addAll(frames)
                    timeline.cycleCount = Timeline.INDEFINITE
                    return true
                }
            }
        }
        return false
    }

    private fun addScore() {
        var temp = 0
        for (i in 0 until size) {
            for (j in 0 until size) {
                if (tableAfter!![i][j] != 0) {
                    temp += Math.pow(2.0, table!![i][j].toDouble()).toInt()
                }
            }
        }
        scoreInt += temp
        tbScore.value=String.format("%d", scoreInt)

    }

    private fun checkLose(): Boolean {
        val s = checkEmptyArea().size
        if (s == 0) {
            if (table!![0][0] != 0) {
                if (table!![0][0] == table!![0][1] || table!![0][0] == table!![1][0]) {
                    return false
                }
            }
            if (table!![0][size - 1] != 0) {
                if (table!![0][size - 1] == table!![0][size - 2] || table!![0][size - 1] == table!![1][size - 1]) {
                    return false
                }
            }
            if (table!![size - 1][size - 1] != 0) {
                if (table!![size - 1][size - 1] == table!![size - 1][size - 2] || table!![size - 1][size - 1] == table!![size - 2][size - 1]) {
                    return false
                }
            }
            if (table!![size - 1][0] != 0) {
                if (table!![size - 1][0] == table!![size - 2][0] || table!![size - 1][0] == table!![size - 1][1]) {
                    return false
                }
            }
            for (i in 1 until size - 1) {
                if (table!![i][0] != 0) {
                    if (table!![i][0] == table!![i - 1][0]
                            || table!![i][0] == table!![i + 1][0]
                            || table!![i][0] == table!![i][1]) {
                        return false
                    }
                }
                if (table!![i][size - 1] != 0) {
                    if (table!![i][size - 1] == table!![i - 1][size - 1]
                            || table!![i][size - 1] == table!![i + 1][size - 1]
                            || table!![i][size - 1] == table!![i][size - 2]) {
                        return false
                    }
                }
                if (table!![0][i] != 0) {
                    if (table!![0][i] == table!![0][i - 1]
                            || table!![0][i] == table!![0][i + 1]
                            || table!![0][i] == table!![1][i]) {
                        return false
                    }
                }
                if (table!![size - 1][i] != 0) {
                    if (table!![size - 1][i] == table!![size - 1][i - 1]
                            || table!![size - 1][i] == table!![size - 1][i + 1]
                            || table!![size - 1][i] == table!![size - 2][i]) {
                        return false
                    }
                }
            }
            for (i in 1 until size - 1) {
                for (j in 1 until size - 1) {
                    if (table!![i][j] != 0) {
                        if (table!![i][j] == table!![i - 1][j]
                                || table!![i][j] == table!![i + 1][j]
                                || table!![i][j] == table!![i][j + 1]
                                || table!![i][j] == table!![i][j - 1]) {
                            return false
                        }
                    }
                }
            }
            return true
        }
        return false
    }

    fun mouseManipulation() {
        if (abs(mPressedPoint!!.mx - mReleasedPoint!!.mx) < 50) {
            if (mReleasedPoint!!.my - mPressedPoint!!.my > 50) {
                processCode(KeyCode.DOWN)
            } else if (mReleasedPoint!!.my - mPressedPoint!!.my < -50) {
                processCode(KeyCode.UP)
            }
        }
        if (abs(mPressedPoint!!.my - mReleasedPoint!!.my) < 50) {
            if (mReleasedPoint!!.mx - mPressedPoint!!.mx > 50) {
                processCode(KeyCode.RIGHT)
            } else if (mReleasedPoint!!.mx - mPressedPoint!!.mx < -50) {
                processCode(KeyCode.LEFT)
            }
        }
    }

    class Point(internal var mx: Int, internal var my: Int)

}

internal enum class State {
    RUNNING,
    GAMEOVER,
    GAMESUCCESS
}

internal class MoveAnime(ctrl: X2048Service, var mPane: StackPane, dx: Int, dy: Int) {
    var mFromX: Double = 0.0
    var mFromY: Double = 0.0
    var mMoveX: Double = 0.0
    var mMoveY: Double = 0.0

    init {
        mFromX = mPane.layoutX
        mFromY = mPane.layoutY
        mMoveX = ctrl.width / ctrl.size * dx
        mMoveY = ctrl.height / ctrl.size * dy
    }
}